/** 
    
    This file is part of Critter.
    
    Copyright (c) 2009 Robin Southern, http://www.nxogre.org
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
*/

                                                                                       

#ifndef CRITTER_ANIMATEDCHARACTER_H
#define CRITTER_ANIMATEDCHARACTER_H

#include "CritterStable.h"
#include "CritterCommon.h"

#include "NxOgreCharacterController.h"
#include "CritterAnimatedCharacterDescription.h"

                                                                                       

namespace Critter
{

/*! class. AnimatedCharacter
    desc.
        A AnimatedCharacter is CharacterController that is "rendered" on the screen; much like
        how a Body is to an Actor. CharacterControllers do provide a limited support on Ogre animations,
        and have an internal (overridable) physics system to help Character Controllers walk or run
        on static geometries.
    note.
        This isn't THE way of implementing CharacterControllers with NxOgre and Ogre, just A way. If
        you have anything more specific then you should use this class as a basis, or write your own.
*/
class CritterPublicClass AnimatedCharacter : public NxOgre::CharacterController, public NxOgre::TimeListener
{
 
 public:
  
  friend class RenderSystem;
  
  NXOGRE_GC_FRIEND_NEW5
  NXOGRE_GC_FRIEND_DELETE
  
 public:
   
   /*! function. turn
       desc.
           Request a turn in a direction. If using this function more than once in a timstep, then
           the previous yaw value will be added to this one. If the value exceeds the maximum turning
           rate per timestep then it will be clamped.
   */
   void  turn(const NxOgre::Radian&);

   /*! function. turn
       desc.
           Request a turn in a direction. If using this function more than once in a timstep, then
           the previous yaw value will be added to this one. If the value exceeds the maximum turning
           rate per timestep then it will be clamped.
   */
   void  turn(const Ogre::Radian&);

   /*! function. move
       desc.
           Request a move in one or more directions. If using this function more than once in a timestep,
           then the previous move vector will be added to this one and normalised.
           
           Generally:
             +/- X direction will be calculated as; walking_velocity + (x * (running_velocity - walking_velocity))
             +   Y direction will be calculated as; y * jump_velocity
             +/- Z direction will be calculated as; walking_velocity + (z * (running_velocity - walking_velocity))
           
           If the character is in the air then (of the ground for a minimal of 0.05 seconds)
             - Any Y value will be ignored
             - X and Z (horziontal) will be calculated as; x_or_z * mMaxAirSpeed
           
           Gravity acceleration (mScene->getGravity() * gravity_modifier) if needed.
       note.
           The move will not happen until the character controller is told to do so; via the 
           advance function. Also do not multiply the vector by time, this will be taken into
           account in the advance function.
   */
   void move(const NxOgre::Vec3& normalised_movement_vector);
   
   /*! function. move
       desc.
           Request a move in one or more directions. If using this function more than once in a timestep,
           then the previous move vector will be added to this one and normalised.
           
           Generally:
             +/- X direction will be calculated as; walking_velocity + (x * (running_velocity - walking_velocity))
             +   Y direction will be calculated as; y * jump_velocity
             +/- Z direction will be calculated as; walking_velocity + (z * (running_velocity - walking_velocity))
           
           If the character is in the air then (of the ground for a minimal of 0.05 seconds)
             - Any Y value will be ignored
             - X and Z (horziontal) will be calculated as; x_or_z * mMaxAirSpeed
           
           Gravity acceleration (mScene->getGravity() * gravity_modifier) if needed.
       note.
           The move will not happen until the character controller is told to do so; via the 
           advance function. 
   */
   void move(const Ogre::Vector3& normalised_movement_vector);

   /*! function. getMoveVector
       desc.
           Get the current moving normalised vector.
       note.
           This doesn't represent the velocity or target position, just the general direction of where the character
           will attempt to go, and how quickly it should get there.
   */
   NxOgre::Vec3 getMoveVector() const;

   /*! function. clearMoveVector
       desc.
           Cancel any requested movement of this timestep
   */
   void clearMoveVector();

   /*! function. getStrideVelocity
       desc.
           Get the rate of horiziontal movement in m/s. Stride must be normalised (0.0 -- 1.0)
       note.
           Stride velocity is calculated as; walking_velocity + (stride * (running_velocity - walking_velocity))
   */
   NxOgre::Real  getHorizontalVelocity(NxOgre::Real stride) const;
   
   /*! function. getRunningVelocity
       desc.
           Get the velocity when the character is running/stride is at 1.0
   */
   NxOgre::Real  getRunningVelocity() const;
   
   /*! function. getRunningVelocity
       desc.
           Get the velocity when the character is running/stride is at 1.0
   */
   void  setRunningVelocity(NxOgre::Real);
   
   /*! function. getWalkingVelocity
       desc.
           Get the velocity when the character is running/stride is at 0.0
   */
   NxOgre::Real  getWalkingVelocity() const;
   
   /*! function. getWalkingVelocity
       desc.
           Get the velocity when the character is running/stride is at 0.0
   */
   void  setWalkingVelocity(NxOgre::Real);
   
   /*! function. getGravityModifier
       desc.
           Get the modifier for gravity.
           0.0 - The character controller does not do gravity.
           0.5 - Is half way between
           1.0 - The character controller accelerates to the ground according to mScene->getGravity()
   */
   NxOgre::Real  getGravityModifier() const;
   
   /*! function. setGravityModifier
       desc.
           Get the modifier for gravity.
           0.0 - The character controller does not do gravity.
           0.5 - Is half way between
           1.0 - The character controller accelerates to the ground according to mScene->getGravity()
   */
   void  setGravityModifier(NxOgre::Real);
   
   /*! function. getGravity
       desc.
           Set the jump velocity.
   */
   NxOgre::Real  getJumpVelocity() const;
   
   /*! function. setGravity
       desc.
           Set the jump velocity.
   */
   void  setJumpVelocity(NxOgre::Real);
   
   
   
   /*! function. getRigidBodyType
       desc.
           Returns Critter::Enums::RigidbodyType_AnimatedCharacter
   */
   virtual unsigned int getRigidBodyType() const;
   
   /*! function. getSceneManager
       desc.
           Get the SceneManager that the SceneNode is in.
   */
   Ogre::SceneManager* getSceneManager();
   
   /*! function. getSceneNode
       desc.
           Get the SceneNode is in, or NULL if there isn't one.
   */
   Ogre::SceneNode* getSceneNode();
    
   /*! function. setSceneNode
       desc.
           Replace the existing SceneNode with a new one.
       note.
           The existing SceneNode may be destroyed according to preset SceneNodeDestructorBehaviour
   */
   void setSceneNode(Ogre::SceneNode*, Enums::SceneNodeDestructorBehaviour = Enums::SceneNodeDestructorBehaviour_Inherit);
   
   /*! function. getSceneNodeDestructorBehaviour
       desc.
           Get what to do when the need is to delete or replace the SceneNode.
   */
   Enums::SceneNodeDestructorBehaviour getSceneNodeDestructorBehaviour() const;
   
   /*! function. setSceneNodeDestructorBehaviour
       desc.
           Set what to do when the need is to delete or replace the SceneNode.
   */
   void setSceneNodeDestructorBehaviour(Enums::SceneNodeDestructorBehaviour);
   
   /*! function. advance
       desc.
           Simulation and Rendering function for the CharacterController
   */
   virtual bool advance(float, const NxOgre::Enums::Priority&, const NxOgre::Enums::SceneFunction&);
   
   /*! function. getHeadPosition
       desc.
           Get the position of the top of the "head" of the character.
   */
   NxOgre::Vec3  getHeadPosition() const;
   
   /*! function. getFootPosition
       desc.
           Get the position of the bottom of the "foot" of the character.
   */
   NxOgre::Vec3  getFootPosition() const;
   
   /*! function. getRenderPosition
       desc.
           Where the SceneNode should be in takening into account of step and extents of the character
   */
   NxOgre::Vec3  getRenderPosition() const;
   
 protected:
   
   /*
     Private Constructor -- Use RenderSystem::createAnimatedCharacter
   */
   AnimatedCharacter(const NxOgre::Vec3&, const NxOgre::Radian&, const NxOgre::SimpleShape&, const AnimatedCharacterDescription&, RenderSystem*);
   
   /*
     Private Destructor -- Use RenderSystem::destroyAnimatedCharacter
   */
   virtual ~AnimatedCharacter();
   
   /*
     Destroy the Scenenode, children and attached objects - or not, according to the SceneNodeDestructorBehaviour.
   */
   void _destructNode(Enums::SceneNodeDestructorBehaviour);
   
 protected:
   
   
   void  _processSituation(float deltaTime);
   
   void  _processRendering(float deltaTime);
   
   
 protected:
     
     RenderSystem*                         mRenderSystem;                 //< \brief AnimatedCharacter's Rendersystem
     Ogre::SceneManager*                   mSceneManager;                 //< \brief Scenenode's SceneManager.
     Ogre::SceneNode*                      mNode;                         //< \brief Scenenode itself..
     NxOgre::Enums::Priority               mRenderPriority;               //< \brief Render priority.
     Enums::SceneNodeDestructorBehaviour   mSceneNodeDestructorBehaviour; //< \brief Behaviour when deleting the SceneNode.
     
     NxOgre::Vec3                          mUpAxis;
     NxOgre::Radian                        mYaw;
     NxOgre::Radian                        mTurningYaw;
     NxOgre::Radian                        mMaxTurningYaw;
     NxOgre::Quat                          mDirection;
     NxOgre::Real                          mWalkingVelocity;
     NxOgre::Real                          mRunningVelocity;
     NxOgre::Real                          mRunningWalkingDiff;
     NxOgre::Real                          mJumpVelocity;
     NxOgre::Real                          mHorizontalAirModifier;
     NxOgre::Real                          mGravityModifier;
     NxOgre::Real                          mAirTimer;
     bool                                  mInAir;
     NxOgre::Vec3                          mMoveVector;
     NxOgre::Vec3                          mMomentum;
     NxOgre::Vec3                          mDimensions;
     bool                                  mIsCapsule;
     NxOgre::Vec3                          mNodeOffset;
};

} // namespace

                                                                                       

#endif
